<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="第24篇：面向对象魔法方法, 张亚飞,飞凡空间,国学,Python,Go">
    <meta name="description" content="学习目标：了解Python中魔法方法的作用，掌握常用魔法方法的概念和应用场景。
之前我们说过Python面向对象中以双下划线开头和结尾的方法为特殊方法，也称魔法方法，是Python中最优美的特性之一。什么是魔法方法呢？它们在面向对象的Pyt">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
	<meta name="baidu-site-verification" content="code-w2ezfaoXE0" />
    <title>第24篇：面向对象魔法方法 | 飞凡空间</title>
    <link rel="icon" type="image/png" href="/medias/my-logo.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">
    
    <script src="/libs/jquery/jquery.min.js"></script>
    
<meta name="generator" content="Hexo 5.2.0">
<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
<link rel="alternate" href="/atom.xml" title="飞凡空间" type="application/atom+xml">
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css">
<link rel="stylesheet" href="/css/prism-line-numbers.css" type="text/css"></head>


<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/my-logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">飞凡空间</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>标签</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/friends" class="waves-effect waves-light">
      
      <i class="fas fa-address-book" style="zoom: 0.6;"></i>
      
      <span>友情链接</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/contact" class="waves-effect waves-light">
      
      <i class="fas fa-comments" style="zoom: 0.6;"></i>
      
      <span>留言板</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>

<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/my-logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">飞凡空间</div>
        <div class="logo-desc">
            
            诚者，天之道也；诚之者，人之道也。诚者，不勉而中，不思而得。从容中道，圣人也；诚之者，择善而固执者也。
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			标签
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/friends" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-address-book"></i>
			
			友情链接
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/contact" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-comments"></i>
			
			留言板
		</a>
          
        </li>
        
        
    </ul>
</div>

        </div>

        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('https://zhangyafei-1258643511.cos.ap-nanjing.myqcloud.com/Python%E6%96%87%E6%A1%A3/assets/24.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <div class="description center-align post-title">
                        第24篇：面向对象魔法方法
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        margin: 35px 0 15px 0;
        padding-left: 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #toc-content .is-active-link::before {
        background-color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/Python%E4%B9%8B%E8%B7%AF/">
                                <span class="chip bg-color">Python之路</span>
                            </a>
                        
                            <a href="/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/">
                                <span class="chip bg-color">面向对象</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/Python/" class="post-category">
                                Python
                            </a>
                        
                            <a href="/categories/Python/base/" class="post-category">
                                base
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2020-12-24
                </div>
                

                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    10.2k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    38 分
                </div>
                
				
                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
            
        </div>
        <hr class="clearfix">
        <div class="card-content article-card-content">
            <div id="articleContent">
                <p><strong>学习目标</strong>：了解Python中魔法方法的作用，掌握常用魔法方法的概念和应用场景。</p>
<p>之前我们说过Python面向对象中以双下划线开头和结尾的方法为特殊方法，也称魔法方法，是Python中最优美的特性之一。什么是魔法方法呢？它们在面向对象的Python的处处皆是。它们是一些可以让你对类添加“魔法”的特殊方法。 它们经常是两个下划线包围来命名的（比如 <code>__init__</code>， <code>__lt__</code> ）。所有的魔法方法都会在Python的官方文档中找到，但是它们组织松散。而且很少会有示例（有的是无聊的语法描述， 语言参考）。本节内容将试图带大家将Python魔法方法搞清楚。</p>
<h3 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h3><p>我们最为熟知的基本的魔法方法就是 <code>__init__</code> ，我们可以用它来指明一个对象初始化的行为。然而，当我们调用 x = SomeClass() 的时候， <code>__init__</code> 并不是第一个被调用的方法。事实上，第一个被调用的是 <code>__new__</code> ，这个 方法才真正地创建了实例。当这个对象的生命周期结束的时候， <code>__del__</code> 会被调用。让我们近一步理解这三个方法：</p>
<ul>
<li><p><code>__new__</code>(cls,[…)</p>
<p><code>__new__</code> 是对象实例化时第一个调用的方法，它只取下 cls 参数，并把其他参数传给 <code>__init__</code> 。 <code>__new__</code> 很少使用，但是也有它适合的场景，尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。我不打算深入讨论<strong>new</strong>` ，因为它并不是很有用， <a target="_blank" rel="noopener" href="http://www.python.org/download/releases/2.2/descrintro/#__new__">Python文档</a> 中 有详细的说明。</p>
</li>
<li><p><code>__init__</code>(self,[…])</p>
<p>类的初始化方法。它获取任何传给构造器的参数（比如我们调用 x = SomeClass(10, ‘foo’) ， <code>__init__</code> 就会接到参数 10 和 ‘foo’ 。 <code>__init__</code>在Python的类定义中用的最多。</p>
</li>
<li><p><code>__del__</code>(self)</p>
<p><code>__new__</code> 和 <code>__init__</code> 是对象的构造器， <code>__del__</code> 是对象的销毁器。它并非实现了语句 del x (因此该语句不等同于 x.<code>__del__</code>())。而是定义了当对象被垃圾回收时的行为。 当对象需要在销毁时做一些处理的时候这个方法很有用，比如 socket 对象、文件对象。但是需要注意的是，当Python解释器退出但对象仍然存活的时候， <code>__del__</code> 并不会 执行。 所以养成一个手工清理的好习惯是很重要的，比如及时关闭连接。</p>
</li>
</ul>
<p>这里有个 <code>__init__</code> 和 <code>__del__</code> 的例子:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> os<span class="token punctuation">.</span>path <span class="token keyword">import</span> join

<span class="token keyword">class</span> <span class="token class-name">FileObject</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''文件对象的装饰类，用来保证文件被删除时能够正确关闭。'''</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> filepath<span class="token operator">=</span><span class="token string">'~'</span><span class="token punctuation">,</span> filename<span class="token operator">=</span><span class="token string">'sample.txt'</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 使用读写模式打开filepath中的filename文件</span>
        self<span class="token punctuation">.</span>file <span class="token operator">=</span> open<span class="token punctuation">(</span>join<span class="token punctuation">(</span>filepath<span class="token punctuation">,</span> filename<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">'r+'</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__del__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>file<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">del</span> self<span class="token punctuation">.</span>file<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="操作符"><a href="#操作符" class="headerlink" title="操作符"></a>操作符</h3><p>使用Python魔法方法的一个巨大优势就是可以构建一个拥有Python内置类型行为的对象。这意味着你可以避免使用非标准的、丑陋的方式来表达简单的操作。 在一些语言中，这样做很常见:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">if</span> instance<span class="token punctuation">.</span>equals<span class="token punctuation">(</span>other_instance<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># do something</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>你当然可以在Python也这么做，但是这样做让代码变得冗长而混乱。不同的类库可能对同一种比较操作采用不同的方法名称，这让使用者需要做很多没有必要的工作。运用魔法方法的魔力，我们可以定义方法 `<strong>eq</strong></p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">if</span> instance <span class="token operator">==</span> other_instance<span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># do something</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>这是魔法力量的一部分，这样我们就可以创建一个像内建类型那样的对象了！</p>
<h4 id="比较操作符"><a href="#比较操作符" class="headerlink" title="比较操作符"></a>比较操作符</h4><p>Python包含了一系列的魔法方法，用于实现对象之间直接比较，而不需要采用方法调用。同样也可以重载Python默认的比较方法，改变它们的行为。下面是这些方法的列表：</p>
<ul>
<li><p><code>__cmp__</code>(self, other)</p>
<p><code>__cmp__</code> 是所有比较魔法方法中最基础的一个，它实际上定义了所有比较操作符的行为（&lt;,==,!=,等等），但是它可能不能按照你需要的方式工作（例如，判断一个实例和另一个实例是否相等采用一套标准，而与判断一个实例是否大于另一实例采用另一套）。 <code>__cmp__</code> 应该在 self &lt; other 时返回一个负整数，在self == other 时返回0，在 self &gt; other 时返回正整数。最好只定义你所需要的比较形式，而不是一次定义全部。 如果你需要实现所有的比较形式，而且它们的判断标准类似，那么 <code>__cmp__</code> 是一个很好的方法，可以减少代码重复，让代码更简洁。</p>
</li>
<li><p><code>__eq__</code>(self, other)</p>
<p>定义等于操作符(==)的行为。</p>
</li>
<li><p><code>__ne__</code>(self, other)</p>
<p>定义不等于操作符(!=)的行为。</p>
</li>
<li><p><code>__lt__</code>(self, other)</p>
<p>定义小于操作符(&lt;)的行为。</p>
</li>
<li><p><code>__gt__</code>(self, other)</p>
<p>定义大于操作符(&gt;)的行为。</p>
</li>
<li><p><code>__le__</code>(self, other)</p>
<p>定义小于等于操作符(&lt;)的行为。</p>
</li>
<li><p><code>__ge__</code>(self, other)</p>
<p>定义大于等于操作符(&gt;)的行为。</p>
</li>
</ul>
<p>举个例子，假如我们想用一个类来存储单词。我们可能想按照字典序（字母顺序）来比较单词，字符串的默认比较行为就是这样。我们可能也想按照其他规则来比较字符串，像是长度，或者音节的数量。在这个例子中，我们使用长度作为比较标准，下面是一种实现:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Word</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''单词类，按照单词长度来定义比较行为'''</span>

    <span class="token keyword">def</span> __new__`<span class="token punctuation">(</span>cls<span class="token punctuation">,</span> word<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 注意，我们只能使用 `__new__` ，因为str是不可变类型</span>
        <span class="token comment" spellcheck="true"># 所以我们必须提前初始化它（在实例创建时）</span>
        <span class="token keyword">if</span> <span class="token string">' '</span> <span class="token keyword">in</span> word<span class="token punctuation">:</span>
            <span class="token keyword">print</span> <span class="token string">"Value contains spaces. Truncating to first space."</span>
            word <span class="token operator">=</span> word<span class="token punctuation">[</span><span class="token punctuation">:</span>word<span class="token punctuation">.</span>index<span class="token punctuation">(</span><span class="token string">' '</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
            <span class="token comment" spellcheck="true"># Word现在包含第一个空格前的所有字母</span>
        <span class="token keyword">return</span> str<span class="token punctuation">.</span>`__new__`<span class="token punctuation">(</span>cls<span class="token punctuation">,</span> word<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__gt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> len<span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">></span> len<span class="token punctuation">(</span>other<span class="token punctuation">)</span>
    <span class="token keyword">def</span> <span class="token function">__lt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> len<span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">&lt;</span> len<span class="token punctuation">(</span>other<span class="token punctuation">)</span>
    <span class="token keyword">def</span> <span class="token function">__ge__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> len<span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">>=</span> len<span class="token punctuation">(</span>other<span class="token punctuation">)</span>
    <span class="token keyword">def</span> <span class="token function">__le__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> len<span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">&lt;=</span> len<span class="token punctuation">(</span>other<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>现在我们可以创建两个 Word 对象（ Word(‘foo’) 和 Word(‘bar’))然后根据长度来比较它们。注意我们没有定义 <code>__eq__</code> 和 <code>__ne__</code> ，这是因为有时候它们会导致奇怪的结果（很明显， Word(‘foo’) == Word(‘bar’) 得到的结果会是true）。根据长度测试是否相等毫无意义，所以我们使用 str 的实现来比较相等。</p>
<p>从上面可以看到，不需要实现所有的比较魔法方法，就可以使用丰富的比较操作。标准库还在 functools 模块中提供了一个类装饰器，只要我们定义 <code>__eq__</code> 和另外一个操作符（ <code>__gt__, </code><strong>lt</strong>` 等），它就可以帮我们实现比较方法。这个特性只在 Python 2.7 中可用。当它可用时，它能帮助我们节省大量的时间和精力。要使用它，只需要它 @total_ordering 放在类的定义之上就可以了</p>
<h4 id="数值操作符"><a href="#数值操作符" class="headerlink" title="数值操作符"></a>数值操作符</h4><p>就像你可以使用比较操作符来比较类的实例，你也可以定义数值操作符的行为。固定好你的安全带，这样的操作符真的有很多。看在组织的份上，我把它们分成了五类：一元操作符，常见算数操作符，反射算数操作符（后面会涉及更多），增强赋值操作符，和类型转换操作符。</p>
<h5 id="一元操作符"><a href="#一元操作符" class="headerlink" title="一元操作符"></a>一元操作符</h5><p>一元操作符只有一个操作符。</p>
<ul>
<li><p><code>__pos__</code>(self)</p>
<p>实现取正操作，例如 +some_object。</p>
</li>
<li><p><code>__neg__</code>(self)</p>
<p>实现取负操作，例如 -some_object。</p>
</li>
<li><p><code>__abs__</code>(self)</p>
<p>实现内建绝对值函数 abs() 操作。</p>
</li>
<li><p><code>__invert__</code>(self)</p>
<p>实现取反操作符 ~。</p>
</li>
<li><p><code>__round__</code>(self， n)</p>
<p>实现内建函数 round() ，n 是近似小数点的位数。</p>
</li>
<li><p><code>__floor__</code>(self)</p>
<p>实现 math.floor() 函数，即向下取整。</p>
</li>
<li><p><code>__ceil__</code>(self)</p>
<p>实现 math.ceil() 函数，即向上取整。</p>
</li>
<li><p><code>__trunc__</code>(self)</p>
<p>实现 math.trunc() 函数，即距离零最近的整数。</p>
</li>
</ul>
<h5 id="常见算数操作符"><a href="#常见算数操作符" class="headerlink" title="常见算数操作符"></a>常见算数操作符</h5><p>现在，我们来看看常见的二元操作符（和一些函数），像+，-，*之类的，它们很容易从字面意思理解。</p>
<ul>
<li><p><code>__add__</code>(self, other)</p>
<p>实现加法操作。</p>
</li>
<li><p><code>__sub__</code>(self, other)</p>
<p>实现减法操作。</p>
</li>
<li><p><code>__mul__</code>(self, other)</p>
<p>实现乘法操作。</p>
</li>
<li><p><code>__floordiv__</code>(self, other)</p>
<p>实现使用 // 操作符的整数除法。</p>
</li>
<li><p><code>__div__</code>(self, other)</p>
<p>实现使用 / 操作符的除法。</p>
</li>
<li><p><code>__truediv__</code>(self, other)</p>
<p>实现 <em>true</em> 除法，这个函数只有使用 from <code>__future__</code> import division 时才有作用。</p>
</li>
<li><p><code>__mod__</code>(self, other)</p>
<p>实现 % 取余操作。</p>
</li>
<li><p><code>__divmod__</code>(self, other)</p>
<p>实现 divmod 内建函数。</p>
</li>
<li><p><code>__pow__</code></p>
<p>实现 ** 操作符。</p>
</li>
<li><p><code>__lshift__</code>(self, other)</p>
<p>实现左移位运算符 &lt;&lt; 。</p>
</li>
<li><p><code>__rshift__</code>(self, other)</p>
<p>实现右移位运算符 &gt;&gt; 。</p>
</li>
<li><p><code>__and__</code>(self, other)</p>
<p>实现按位与运算符 &amp; 。</p>
</li>
<li><p><code>__or__</code>(self, other)</p>
<p>实现按位或运算符 | 。</p>
</li>
<li><p><code>__xor__</code>(self, other)</p>
<p>实现按位异或运算符 ^ 。</p>
</li>
</ul>
<h4 id="反射算数运算符"><a href="#反射算数运算符" class="headerlink" title="反射算数运算符"></a>反射算数运算符</h4><p>还记得刚才我说会谈到反射运算符吗？可能你会觉得它是什么高端霸气上档次的概念，其实这东西挺简单的，下面举个例子:</p>
<pre class="line-numbers language-python"><code class="language-python">some_object <span class="token operator">+</span> other<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>这是“常见”的加法，反射是一样的意思，只不过是运算符交换了一下位置:</p>
<pre class="line-numbers language-python"><code class="language-python">other <span class="token operator">+</span> some_object<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>所有反射运算符魔法方法和它们的常见版本做的工作相同，只不过是处理交换连个操作数之后的情况。绝大多数情况下，反射运算和正常顺序产生的结果是相同的，所以很可能你定义 <code>__radd__</code> 时只是调用一下 <code>__add__。注意一点，操作符左侧的对象（也就是上面的 other ）一定不要定义（或者产生 NotImplemented 异常） 操作符的非反射版本。例如，在上面的例子中，只有当 other 没有定义 </code><strong>add</strong><code> 时some_object.</code><strong>radd</strong>` 才会被调用。</p>
<ul>
<li><p><code>__radd__</code>(self, other)</p>
<p>实现反射加法操作。</p>
</li>
<li><p><code>__rsub__</code>(self, other)</p>
<p>实现反射减法操作。</p>
</li>
<li><p><code>__rmul__</code>(self, other)</p>
<p>实现反射乘法操作。</p>
</li>
<li><p><code>__rfloordiv__</code>(self, other)</p>
<p>实现使用 // 操作符的整数反射除法。</p>
</li>
<li><p><code>__rdiv__</code>(self, other)</p>
<p>实现使用 / 操作符的反射除法。</p>
</li>
<li><p><code>__rtruediv__</code>(self, other)</p>
<p>实现 <em>true</em> 反射除法，这个函数只有使用 from <code>__future__</code> import division时才有作用。</p>
</li>
<li><p><code>__rmod__</code>(self, other)</p>
<p>实现 % 反射取余操作符。</p>
</li>
<li><p><code>__rdivmod__</code>(self, other)</p>
<p>实现调用 divmod(other, self) 时 divmod 内建函数的操作。</p>
</li>
<li><p><code>__rpow__</code></p>
<p>实现 ** 反射操作符。</p>
</li>
<li><p><code>__rlshift__</code>(self, other)</p>
<p>实现反射左移位运算符 &lt;&lt; 的作用。</p>
</li>
<li><p><code>__rshift__</code>(self, other)</p>
<p>实现反射右移位运算符 &gt;&gt; 的作用。</p>
</li>
<li><p><code>__rand__</code>(self, other)</p>
<p>实现反射按位与运算符 &amp; 。</p>
</li>
<li><p><code>__ror__</code>(self, other)</p>
<p>实现反射按位或运算符 | 。</p>
</li>
<li><p><code>__rxor</code>__(self, other)</p>
<p>实现反射按位异或运算符 ^ 。</p>
</li>
</ul>
<h4 id="增强赋值运算符"><a href="#增强赋值运算符" class="headerlink" title="增强赋值运算符"></a>增强赋值运算符</h4><p>Python同样提供了大量的魔法方法，可以用来自定义增强赋值操作的行为。或许你已经了解增强赋值，它融合了“常见”的操作符和赋值操作，如果你还是没听明白，看下面的例子:</p>
<pre class="line-numbers language-python"><code class="language-python">x <span class="token operator">=</span> <span class="token number">5</span>
x <span class="token operator">+=</span> <span class="token number">1</span> <span class="token comment" spellcheck="true"># 也就是 x = x + 1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>这些方法都应该返回左侧操作数应该被赋予的值（例如， a += b <code>__iadd__</code> 也许会返回 a + b ，这个结果会被赋给 a ）,下面是方法列表：</p>
<ul>
<li><p><code>__iadd__</code>(self, other)</p>
<p>实现加法赋值操作。</p>
</li>
<li><p><code>__isub__</code>(self, other)</p>
<p>实现减法赋值操作。</p>
</li>
<li><p><code>__imul__</code>(self, other)</p>
<p>实现乘法赋值操作。</p>
</li>
<li><p><code>__ifloordiv__</code>(self, other)</p>
<p>实现使用 //= 操作符的整数除法赋值操作。</p>
</li>
<li><p><code>__idiv__</code>(self, other)</p>
<p>实现使用 /= 操作符的除法赋值操作。</p>
</li>
<li><p><code>__itruediv__</code>(self, other)</p>
<p>实现 <em>true</em> 除法赋值操作，这个函数只有使用 from <code>__future__</code> import division 时才有作用。</p>
</li>
<li><p><code>__imod</code>__(self, other)</p>
<p>实现 %= 取余赋值操作。</p>
</li>
<li><p><code>__ipow</code>__</p>
<p>实现 **= 操作。</p>
</li>
<li><p><code>__ilshift__</code>(self, other)</p>
<p>实现左移位赋值运算符 &lt;&lt;= 。</p>
</li>
<li><p><code>__irshift__</code>(self, other)</p>
<p>实现右移位赋值运算符 &gt;&gt;= 。</p>
</li>
<li><p><code>__iand__</code>(self, other)</p>
<p>实现按位与运算符 &amp;= 。</p>
</li>
<li><p><code>__ior__</code>(self, other)</p>
<p>实现按位或赋值运算符 | 。</p>
</li>
<li><p><code>__ixor__</code>(self, other)</p>
<p>实现按位异或赋值运算符 ^= 。</p>
</li>
</ul>
<h4 id="类型转换操作符"><a href="#类型转换操作符" class="headerlink" title="类型转换操作符"></a>类型转换操作符</h4><p>Python也有一系列的魔法方法用于实现类似 float() 的内建类型转换函数的操作。它们是这些：</p>
<ul>
<li><p><code>__int__</code>(self)</p>
<p>实现到int的类型转换。</p>
</li>
<li><p><code>__long__</code>(self)</p>
<p>实现到long的类型转换。</p>
</li>
<li><p><code>__float__</code>(self)</p>
<p>实现到float的类型转换。</p>
</li>
<li><p><code>__complex__</code>(self)</p>
<p>实现到complex的类型转换。</p>
</li>
<li><p><code>__oct__</code>(self)</p>
<p>实现到八进制数的类型转换。</p>
</li>
<li><p><code>__hex__</code>(self)</p>
<p>实现到十六进制数的类型转换。</p>
</li>
<li><p><code>__index__</code>(self)</p>
<p>实现当对象用于切片表达式时到一个整数的类型转换。如果你定义了一个可能会用于切片操作的数值类型，你应该定义 `__index__。</p>
</li>
<li><p><code>__trunc__</code>(self)</p>
<p>当调用 math.trunc(self) 时调用该方法， <code>__trunc__</code> 应该返回 self 截取到一个整数类型（通常是long类型）的值。</p>
</li>
<li><p><code>__coerce__</code>(self)</p>
<p>该方法用于实现混合模式算数运算，如果不能进行类型转换， <code>__coerce__</code> 应该返回 None 。反之，它应该返回一个二元组 self 和 other ，这两者均已被转换成相同的类型。</p>
</li>
</ul>
<h3 id="类的表示"><a href="#类的表示" class="headerlink" title="类的表示"></a>类的表示</h3><p>使用字符串来表示类是一个相当有用的特性。在Python中有一些内建方法可以返回类的表示，相对应的，也有一系列魔法方法可以用来自定义在使用这些内建函数时类的行为。</p>
<ul>
<li><p><code>__str__</code>(self)</p>
<p>定义对类的实例调用 str() 时的行为。</p>
</li>
<li><p><code>__repr__</code>(self)</p>
<p>定义对类的实例调用 repr() 时的行为。 str() 和 repr() 最主要的差别在于“目标用户”。 repr() 的作用是产生机器可读的输出（大部分情况下，其输出可以作为有效的Python代码），而 str() 则产生人类可读的输出。</p>
</li>
<li><p><code>__unicode__</code>(self)</p>
<p>定义对类的实例调用 unicode() 时的行为。 unicode() 和 str() 很像，只是它返回unicode字符串。注意，如果调用者试图调用 str() 而你的类只实现了<strong>unicode</strong><code>() ，那么类将不能正常工作。所有你应该总是定义 </code><strong>str</strong>`() ，以防有些人没有闲情雅致来使用unicode。</p>
</li>
<li><p><code>__format__</code>(self)</p>
<p>定义当类的实例用于新式字符串格式化时的行为，例如， “Hello, 0:abc!”.format(a) 会导致调用 a.<code>__format__</code>(“abc”) 。当定义你自己的数值类型或字符串类型时，你可能想提供某些特殊的格式化选项，这种情况下这个魔法方法会非常有用。</p>
</li>
<li><p><code>__hash__</code>(self)</p>
<p>定义对类的实例调用 hash() 时的行为。它必须返回一个整数，其结果会被用于字典中键的快速比较。同时注意一点，实现这个魔法方法通常也需要实现<strong>eq</strong>` ，并且遵守如下的规则： a == b 意味着 hash(a) == hash(b)。</p>
</li>
<li><p><code>__nonzero__</code>(self)</p>
<p>定义对类的实例调用 bool() 时的行为，根据你自己对类的设计，针对不同的实例，这个魔法方法应该相应地返回True或False。</p>
</li>
<li><p><code>__dir__</code>(self)</p>
<p>定义对类的实例调用 dir() 时的行为，这个方法应该向调用者返回一个属性列表。一般来说，没必要自己实现 <code>__dir__</code> 。但是如果你重定义了 <code>__getattr__或者 </code><strong>getattribute</strong>` （下个部分会介绍），乃至使用动态生成的属性，以实现类的交互式使用，那么这个魔法方法是必不可少的。</p>
</li>
</ul>
<p>到这里，我们基本上已经结束了魔法方法指南中无聊并且例子匮乏的部分。既然我们已经介绍了较为基础的魔法方法，是时候涉及更高级的内容了。</p>
<h3 id="访问控制"><a href="#访问控制" class="headerlink" title="访问控制"></a>访问控制</h3><p>很多从其他语言转向Python的人都抱怨Python的类缺少真正意义上的封装（即没办法定义私有属性然后使用公有的getter和setter）。然而事实并非如此。实际上Python不是通过显式定义的字段和方法修改器，而是通过魔法方法实现了一系列的封装。</p>
<ul>
<li><code>__getattr__</code>(self, name)</li>
</ul>
<p>当用户试图访问一个根本不存在（或者暂时不存在）的属性时，你可以通过这个魔法方法来定义类的行为。这个可以用于捕捉错误的拼写并且给出指引，使用废弃属性时给出警告（如果你愿意，仍然可以计算并且返回该属性），以及灵活地处理AttributeError。只有当试图访问不存在的属性时它才会被调用，所以这不能算是一个真正的封装的办法。</p>
<ul>
<li><code>__setattr__</code>(self, name, value)</li>
</ul>
<p>和 <code>__getattr__</code> 不同， <code>__setattr__</code> 可以用于真正意义上的封装。它允许你自定义某个属性的赋值行为，不管这个属性存在与否，也就是说你可以对任意属性的任何变化都定义自己的规则。然后，一定要小心使用 <code>__setattr__</code> ，这个列表最后的例子中会有所展示。</p>
<ul>
<li><code>__delattr__</code>(self, name)</li>
</ul>
<p>这个魔法方法和 <code>__setattr__</code> 几乎相同，只不过它是用于处理删除属性时的行为。和 _setattr__<code> 一样，使用它时也需要多加小心，防止产生无限递归（在__delattr__</code> 的实现中调用 del self.name 会导致无限递归）。</p>
<ul>
<li><code>__getattribute__</code>(self, name)</li>
</ul>
<p><code>__getattribute__</code> 看起来和上面那些方法很合得来，但是最好不要使用它。<code>__getattribute__</code> 只能用于新式类。在最新版的Python中所有的类都是新式类，在老版Python中你可以通过继承 object 来创建新式类。 <code>__getattribute__</code> 允许你自定义属性被访问时的行为，它也同样可能遇到无限递归问题（通过调用基类的<code>__getattribute__</code> 来避免）。 <code>__getattribute__</code> 基本上可以替代 <code>__getattr__</code> 。只有当它被实现，并且显式地被调用，或者产生 <code>AttributeError</code> 时它才被使用。 这个魔法方法可以被使用（毕竟，选择权在你自己），我不推荐你使用它，因为它的使用范围相对有限（通常我们想要在赋值时进行特殊操作，而不是取值时），而且实现这个方法很容易出现Bug。</p>
<p>自定义这些控制属性访问的魔法方法很容易导致问题，考虑下面这个例子:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">__setattr__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">.</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
    self<span class="token punctuation">.</span>name <span class="token operator">=</span> value
    <span class="token comment" spellcheck="true"># 因为每次属性幅值都要调用 `__setattr__`()，所以这里的实现会导致递归</span>
    <span class="token comment" spellcheck="true"># 这里的调用实际上是 self.`__setattr('name', value)。因为这个方法一直</span>
    <span class="token comment" spellcheck="true"># 在调用自己，因此递归将持续进行，直到程序崩溃</span>

<span class="token keyword">def</span> <span class="token function">__setattr__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
    self<span class="token punctuation">.</span>`__dict__<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> value <span class="token comment" spellcheck="true"># 使用 `__dict__` 进行赋值</span>
    <span class="token comment" spellcheck="true"># 定义自定义行为</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>再次重申，Python的魔法方法十分强大，能力越强责任越大，了解如何正确的使用魔法方法更加重要。</p>
<p>到这里，我们对Python中自定义属性存取控制有了什么样的印象？它并不适合轻度的使用。实际上，它有些过分强大，而且违反直觉。然而它之所以存在，是因为一个更大的原则：Python不指望让杜绝坏事发生，而是想办法让做坏事变得困难。自由是至高无上的权利，你真的可以随心所欲。下面的例子展示了实际应用中某些特殊的属性访问方法（注意我们之所以使用 super 是因为不是所有的类都有 <code>__dict__</code>属性）:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">AccessCounter</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">''' 一个包含了一个值并且实现了访问计数器的类
    每次值的变化都会导致计数器自增'''</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> val<span class="token punctuation">)</span><span class="token punctuation">:</span>
            super<span class="token punctuation">(</span>AccessCounter<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">.</span>`__setattr__`<span class="token punctuation">(</span><span class="token string">'counter'</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
            super<span class="token punctuation">(</span>AccessCounter<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">.</span>`__setattr__`<span class="token punctuation">(</span><span class="token string">'value'</span><span class="token punctuation">,</span> val<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__setattr__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">if</span> name <span class="token operator">==</span> <span class="token string">'value'</span><span class="token punctuation">:</span>
                    super<span class="token punctuation">(</span>AccessCounter<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">.</span>`__setattr_<span class="token punctuation">(</span><span class="token string">'counter'</span><span class="token punctuation">,</span> self<span class="token punctuation">.</span>counter <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span>
        <span class="token comment" spellcheck="true"># 使计数器自增变成不可避免</span>
        <span class="token comment" spellcheck="true"># 如果你想阻止其他属性的赋值行为</span>
        <span class="token comment" spellcheck="true"># 产生 AttributeError(name) 就可以了</span>
        super<span class="token punctuation">(</span>AccessCounter<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">.</span>`__setattr__`<span class="token punctuation">(</span>name<span class="token punctuation">,</span> value<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__delattr__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">if</span> name <span class="token operator">==</span> <span class="token string">'value'</span><span class="token punctuation">:</span>
                    super<span class="token punctuation">(</span>AccessCounter<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">.</span>`__setattr<span class="token punctuation">(</span><span class="token string">'counter'</span><span class="token punctuation">,</span> self<span class="token punctuation">.</span>counter <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span>
                    super<span class="token punctuation">(</span>AccessCounter<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">.</span>`__delattr<span class="token punctuation">(</span>name<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="自定义序列"><a href="#自定义序列" class="headerlink" title="自定义序列"></a>自定义序列</h3><p>有许多办法可以让你的Python类表现得像是内建序列类型（字典，元组，列表，字符串等）。这些魔法方式是目前为止我最喜欢的。它们给了你难以置信的控制能力，可以让你的类与一系列的全局函数完美结合。在了解激动人心的内容之前，首先你需要掌握一些预备知识。</p>
<h5 id="预备知识"><a href="#预备知识" class="headerlink" title="预备知识"></a>预备知识</h5><p>既然讲到创建自己的序列类型，就不得不说一说协议了。协议类似某些语言中的接口，里面包含的是一些必须实现的方法。在Python中，协议完全是非正式的，也不需要显式的声明，事实上，它们更像是一种参考标准。</p>
<p>为什么我们要讲协议？因为在Python中实现自定义容器类型需要用到一些协议。首先，不可变容器类型有如下协议：想实现一个不可变容器，你需要定义 <code>__len__</code> 和<code>__getitem__</code> (后面会具体说明）。可变容器的协议除了上面提到的两个方法之外，还需要定义 ``<strong>setitem</strong><code>和</code><strong>delitem</strong><code>。最后，如果你想让你的对象可以迭代，你需要定义</code><strong>iter</strong><code>，这个方法返回一个迭代器。迭代器必须遵守迭代器协议，需要定义</code><strong>iter</strong>` （返回它自己）和 next 方法。</p>
<h5 id="容器背后的魔法方法"><a href="#容器背后的魔法方法" class="headerlink" title="容器背后的魔法方法"></a>容器背后的魔法方法</h5><ul>
<li><p><code>__len__</code>(self)</p>
<p>返回容器的长度，可变和不可变类型都需要实现。</p>
</li>
<li><p><code>__getitem__(</code>self, key)</p>
<p>定义对容器中某一项使用 self[key] 的方式进行读取操作时的行为。这也是可变和不可变容器类型都需要实现的一个方法。它应该在键的类型错误式产生TypeError 异常，同时在没有与键值相匹配的内容时产生 KeyError 异常。</p>
</li>
<li><p><code>__setitem__</code>(self, key)</p>
<p>定义对容器中某一项使用 self[key] 的方式进行赋值操作时的行为。它是可变容器类型必须实现的一个方法，同样应该在合适的时候产生 KeyError 和TypeError 异常。</p>
</li>
<li><p><code>__iter__</code>(self, key)</p>
<p>它应该返回当前容器的一个迭代器。迭代器以一连串内容的形式返回，最常见的是使用 iter() 函数调用，以及在类似 for x in container: 的循环中被调用。迭代器是他们自己的对象，需要定义 <code>__iter__</code> 方法并在其中返回自己。</p>
</li>
<li><p><code>__reversed__</code>(self)</p>
<p>定义了对容器使用 reversed() 内建函数时的行为。它应该返回一个反转之后的序列。当你的序列类是有序时，类似列表和元组，再实现这个方法，</p>
</li>
<li><p><code>__contains__</code>(self, item)</p>
<p><code>__contains__</code> 定义了使用 in 和 not in 进行成员测试时类的行为。你可能好奇为什么这个方法不是序列协议的一部分，原因是，如果 <code>__contains__</code> 没有定义，Python就会迭代整个序列，如果找到了需要的一项就返回 True 。</p>
</li>
<li><p><code>__missing__</code>(self ,key)</p>
<p><code>__missing__</code> 在字典的子类中使用，它定义了当试图访问一个字典中不存在的键时的行为（目前为止是指字典的实例，例如我有一个字典 d ， “george” 不是字典中的一个键，当试图访问 d[“george’] 时就会调用d.<code>__missing__</code>(“george”) ）。</p>
</li>
</ul>
<h5 id="一个例子"><a href="#一个例子" class="headerlink" title="一个例子"></a>一个例子</h5><p>让我们来看一个实现了一些函数式结构的列表，可能在其他语言中这种结构更常见（例如Haskell）:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">FunctionalList</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''一个列表的封装类，实现了一些额外的函数式
    方法，例如head, tail, init, last, drop和take。'''</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> values<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">if</span> values <span class="token keyword">is</span> None<span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>values <span class="token operator">=</span> values

    <span class="token keyword">def</span> <span class="token function">__len__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> len<span class="token punctuation">(</span>self<span class="token punctuation">.</span>values<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__getitem__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 如果键的类型或值不合法，列表会返回异常</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>values<span class="token punctuation">[</span>key<span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">__setitem__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>values<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> value

    <span class="token keyword">def</span> <span class="token function">__delitem__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">del</span> self<span class="token punctuation">.</span>values<span class="token punctuation">[</span>key<span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">__iter__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> iter<span class="token punctuation">(</span>self<span class="token punctuation">.</span>values<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">__reversed__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> reversed<span class="token punctuation">(</span>self<span class="token punctuation">.</span>values<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">append</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>values<span class="token punctuation">.</span>append<span class="token punctuation">(</span>value<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">head</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 取得第一个元素</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>values<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">tail</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 取得除第一个元素外的所有元素</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>valuse<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">init</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 取得除最后一个元素外的所有元素</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>values<span class="token punctuation">[</span><span class="token punctuation">:</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">last</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 取得最后一个元素</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>values<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">drop</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> n<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 取得除前n个元素外的所有元素</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>values<span class="token punctuation">[</span>n<span class="token punctuation">:</span><span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">take</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> n<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 取得前n个元素</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>values<span class="token punctuation">[</span><span class="token punctuation">:</span>n<span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>就是这些，一个（微不足道的）有用的例子，向你展示了如何实现自己的序列。当然啦，自定义序列有更大的用处，而且绝大部分都在标准库中实现了（Python是自带电池的，记得吗？），像 Counter , OrderedDict 和 NamedTuple 。</p>
<h3 id="反射"><a href="#反射" class="headerlink" title="反射"></a>反射</h3><p>你可以通过定义魔法方法来控制用于反射的内建函数 isinstance 和 issubclass 的行为。下面是对应的魔法方法：</p>
<ul>
<li><p><code>__instancecheck__</code>(self, instance)</p>
<p>检查一个实例是否是你定义的类的一个实例（例如 isinstance(instance, class) ）。</p>
</li>
<li><p><code>__subclasscheck__</code>(self, subclass)</p>
<p>检查一个类是否是你定义的类的子类（例如 issubclass(subclass, class)）。</p>
</li>
</ul>
<p>这几个魔法方法的适用范围看起来有些窄，事实也正是如此。我不会在反射魔法方法上花费太多时间，因为相比其他魔法方法它们显得不是很重要。但是它们展示了在Python中进行面向对象编程（或者总体上使用Python进行编程）时很重要的一点：不管做什么事情，都会有一个简单方法，不管它常用不常用。这些魔法方法可能看起来没那么有用，但是当你真正需要用到它们的时候，你会感到很幸运，因为它们还在那儿（也因为你阅读了这本指南！）</p>
<h3 id="抽象基类"><a href="#抽象基类" class="headerlink" title="抽象基类"></a>抽象基类</h3><p>请参考 <a target="_blank" rel="noopener" href="http://docs.python.org/2/library/abc.html">http://docs.python.org/2/library/abc.html</a>.</p>
<h3 id="可调用的对象"><a href="#可调用的对象" class="headerlink" title="可调用的对象"></a>可调用的对象</h3><p>你可能已经知道了，在Python中，函数是一等的对象。这意味着它们可以像其他任何对象一样被传递到函数和方法中，这是一个十分强大的特性。</p>
<p>Python中一个特殊的魔法方法允许你自己类的对象表现得像是函数，然后你就可以“调用”它们，把它们传递到使用函数做参数的函数中，等等等等。这是另一个强大而且方便的特性，让使用Python编程变得更加幸福。</p>
<ul>
<li><p><code>__call__</code>(self, [args…])</p>
<p>允许类的一个实例像函数那样被调用。本质上这代表了 x() 和 x.<code>__call__</code>()是相同的。注意 <code>__call__</code> 可以有多个参数，这代表你可以像定义其他任何函数一样，定义 <code>__call__</code> ，喜欢用多少参数就用多少。</p>
</li>
</ul>
<p><code>__call__</code> 在某些需要经常改变状态的类的实例中显得特别有用。“调用”这个实例来改变它的状态，是一种更加符合直觉，也更加优雅的方法。一个表示平面上实体的类是一个不错的例子:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Entity</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''表示一个实体的类，调用它的实例可以更新实体的位置'''</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> size<span class="token punctuation">,</span> x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>x<span class="token punctuation">,</span> self<span class="token punctuation">.</span>y <span class="token operator">=</span> x<span class="token punctuation">,</span> y
        self<span class="token punctuation">.</span>size <span class="token operator">=</span> size

        <span class="token keyword">def</span> <span class="token function">__call__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token triple-quoted-string string">'''改变实体的位置'''</span>
            self<span class="token punctuation">.</span>x<span class="token punctuation">,</span> self<span class="token punctuation">.</span>y <span class="token operator">=</span> x<span class="token punctuation">,</span> y<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="上下文管理器"><a href="#上下文管理器" class="headerlink" title="上下文管理器"></a>上下文管理器</h3><p>在Python 2.5中引入了一个全新的关键词，随之而来的是一种新的代码复用方法——with 声明。上下文管理的概念在Python中并不是全新引入的（之前它作为标准库的一部分实现），直到PEP 343被接受，它才成为一种一级的语言结构。可能你已经见过这种写法了:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">with</span> open<span class="token punctuation">(</span><span class="token string">'foo.txt'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> bar<span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># 使用bar进行某些操作</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>当对象使用 with 声明创建时，上下文管理器允许类做一些设置和清理工作。上下文管理器的行为由下面两个魔法方法所定义：</p>
<ul>
<li><p><code>__enter__</code>(self)</p>
<p>定义使用 with 声明创建的语句块最开始上下文管理器应该做些什么。注意<strong>enter</strong>` 的返回值会赋给 with 声明的目标，也就是 as 之后的东西。</p>
</li>
<li><p><code>__exit__</code>(self, exception_type, exception_value, traceback)</p>
<p>定义当 with 声明语句块执行完毕（或终止）时上下文管理器的行为。它可以用来处理异常，进行清理，或者做其他应该在语句块结束之后立刻执行的工作。如果语句块顺利执行， exception_type , exception_value 和 traceback 会是None 。否则，你可以选择处理这个异常或者让用户来处理。如果你想处理异常，确保 <code>__exit__</code> 在完成工作之后返回 True 。如果你不想处理异常，那就让它发生吧。</p>
</li>
</ul>
<p>对一些具有良好定义的且通用的设置和清理行为的类，<strong>enter</strong><code>和</code><strong>exit</strong>` 会显得特别有用。你也可以使用这几个方法来创建通用的上下文管理器，用来包装其他对象。下面是一个例子:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Closer</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''一个上下文管理器，可以在with语句中
    使用close()自动关闭对象'''</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> obj<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>obj <span class="token operator">=</span> obj

    <span class="token keyword">def</span> <span class="token function">__enter__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> obj<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>obj <span class="token comment" spellcheck="true"># 绑定到目标</span>

    <span class="token keyword">def</span> <span class="token function">__exit__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> exception_type<span class="token punctuation">,</span> exception_value<span class="token punctuation">,</span> traceback<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">try</span><span class="token punctuation">:</span>
             self<span class="token punctuation">.</span>obj<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">except</span> AttributeError<span class="token punctuation">:</span> <span class="token comment" spellcheck="true"># obj不是可关闭的</span>
             <span class="token keyword">print</span> <span class="token string">'Not closable.'</span>
             <span class="token keyword">return</span> <span class="token boolean">True</span> <span class="token comment" spellcheck="true"># 成功地处理了异常</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>这是一个 Closer 在实际使用中的例子，使用一个FTP连接来演示（一个可关闭的socket):</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">from</span> magicmethods <span class="token keyword">import</span> Closer
<span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">from</span> ftplib <span class="token keyword">import</span> FTP
<span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">with</span> Closer<span class="token punctuation">(</span>FTP<span class="token punctuation">(</span><span class="token string">'ftp.somesite.com'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">as</span> conn<span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>         conn<span class="token punctuation">.</span>dir<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token comment" spellcheck="true"># 为了简单，省略了某些输出</span>
<span class="token operator">>></span><span class="token operator">></span> conn<span class="token punctuation">.</span>dir<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment" spellcheck="true"># 很长的 AttributeError 信息，不能使用一个已关闭的连接</span>
<span class="token operator">>></span><span class="token operator">></span> <span class="token keyword">with</span> Closer<span class="token punctuation">(</span>int<span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">as</span> i<span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>         i <span class="token operator">+=</span> <span class="token number">1</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
Not closable<span class="token punctuation">.</span>
<span class="token operator">>></span><span class="token operator">></span> i
<span class="token number">6</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>看到我们的包装器是如何同时优雅地处理正确和不正确的调用了吗？这就是上下文管理器和魔法方法的力量。Python标准库包含一个 <code>contextlib</code> 模块，里面有一个上下文管理器 <code>contextlib.closing() </code>基本上和我们的包装器完成的是同样的事情（但是没有包含任何当对象没有close()方法时的处理）。</p>
<h3 id="创建描述对象"><a href="#创建描述对象" class="headerlink" title="创建描述对象"></a>创建描述对象</h3><p>描述符是一个类，当使用取值，赋值和删除 时它可以改变其他对象。描述符不是用来单独使用的，它们需要被一个拥有者类所包含。描述符可以用来创建面向对象数据库，以及创建某些属性之间互相依赖的类。描述符在表现具有不同单位的属性，或者需要计算的属性时显得特别有用（例如表现一个坐标系中的点的类，其中的距离原点的距离这种属性）。</p>
<p>要想成为一个描述符，一个类必须具有实现 <code>__get__</code> , <code>__set__</code> 和 <code>__delete__</code> 三个方法中至少一个。</p>
<p>让我们一起来看一看这些魔法方法：</p>
<ul>
<li><p><code>__get__</code>(self, instance, owner)</p>
<p>定义当试图取出描述符的值时的行为。 instance 是拥有者类的实例， owner是拥有者类本身。</p>
</li>
<li><p><code>__set__</code>(self, instance, owner)</p>
<p>定义当描述符的值改变时的行为。 instance 是拥有者类的实例， value 是要赋给描述符的值。</p>
</li>
<li><p><code>__delete__</code>(self, instance, owner)</p>
<p>定义当描述符的值被删除时的行为。 instance 是拥有者类的实例</p>
</li>
</ul>
<p>现在，来看一个描述符的有效应用：单位转换:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Meter</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''米的描述符。'''</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> value<span class="token operator">=</span><span class="token number">0.0</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>value <span class="token operator">=</span> float<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
    <span class="token keyword">def</span> <span class="token function">__get__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> instance<span class="token punctuation">,</span> owner<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>value
    <span class="token keyword">def</span> <span class="token function">__set__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> instance<span class="token punctuation">,</span> owner<span class="token punctuation">)</span><span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>value <span class="token operator">=</span> float<span class="token punctuation">(</span>value<span class="token punctuation">)</span>

<span class="token keyword">class</span> <span class="token class-name">Foot</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''英尺的描述符。'''</span>

    <span class="token keyword">def</span> <span class="token function">__get__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> instance<span class="token punctuation">,</span> owner<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> instance<span class="token punctuation">.</span>meter <span class="token operator">*</span> <span class="token number">3.2808</span>
    <span class="token keyword">def</span> <span class="token function">__set__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> instance<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
            instance<span class="token punctuation">.</span>meter <span class="token operator">=</span> float<span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">3.2808</span>

<span class="token keyword">class</span> <span class="token class-name">Distance</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''用于描述距离的类，包含英尺和米两个描述符。'''</span>
    meter <span class="token operator">=</span> Meter<span class="token punctuation">(</span><span class="token punctuation">)</span>
    foot <span class="token operator">=</span> Foot<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="拷贝"><a href="#拷贝" class="headerlink" title="拷贝"></a>拷贝</h3><p>有些时候，特别是处理可变对象时，你可能想拷贝一个对象，改变这个对象而不影响原有的对象。这时就需要用到Python的 copy 模块了。然而（幸运的是），Python模块并不具有感知能力， 因此我们不用担心某天基于Linux的机器人崛起。但是我们的确需要告诉Python如何有效率地拷贝对象。</p>
<ul>
<li><p><code>__copy__</code>(self)</p>
<p>定义对类的实例使用 copy.copy() 时的行为。 copy.copy() 返回一个对象的浅拷贝，这意味着拷贝出的实例是全新的，然而里面的数据全都是引用的。也就是说，对象本身是拷贝的，但是它的数据还是引用的（所以浅拷贝中的数据更改会影响原对象）。</p>
</li>
<li><p>`<strong>deepcopy</strong>(self, memodict=)</p>
<p>定义对类的实例使用 copy.deepcopy() 时的行为。 copy.deepcopy() 返回一个对象的深拷贝，这个对象和它的数据全都被拷贝了一份。 memodict 是一个先前拷贝对象的缓存，它优化了拷贝过程，而且可以防止拷贝递归数据结构时产生无限递归。当你想深拷贝一个单独的属性时，在那个属性上调用 copy.deepcopy()，使用 memodict 作为第一个参数。</p>
</li>
</ul>
<p>这些魔法方法有什么用武之地呢？像往常一样，当你需要比默认行为更加精确的控制时。例如，如果你想拷贝一个对象，其中存储了一个字典作为缓存（可能会很大），拷贝缓存可能是没有意义的。如果这个缓存可以在内存中被不同实例共享，那么它就应该被共享。</p>
<h3 id="Pickling"><a href="#Pickling" class="headerlink" title="Pickling"></a>Pickling</h3><p>如果你和其他的Python爱好者共事过，很可能你已经听说过Pickling了。Pickling是Python数据结构的序列化过程，当你想存储一个对象稍后再取出读取时，Pickling会显得十分有用。然而它同样也是担忧和混淆的主要来源。</p>
<p>Pickling是如此的重要，以至于它不仅仅有自己的模块（ pickle ），还有自己的协议和魔法方法。首先，我们先来简要的介绍一下如何pickle已存在的对象类型（如果你已经知道了，大可跳过这部分内容）。</p>
<h4 id="Pickling-小试牛刀"><a href="#Pickling-小试牛刀" class="headerlink" title="Pickling : 小试牛刀"></a>Pickling : 小试牛刀</h4><p>我们一起来pickle吧。假设你有一个字典，你想存储它，稍后再取出来。你可以把它的内容写入一个文件，小心翼翼地确保使用了正确地格式，要把它读取出来，你可以使用 exec() 或处理文件输入。但是这种方法并不可靠：如果你使用纯文本来存储重要数据，数据很容易以多种方式被破坏或者修改，导致你的程序崩溃，更糟糕的情况下，还可能在你的计算机上运行恶意代码。因此，我们要pickle它:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">import</span> pickle

data <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">'foo'</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
                <span class="token string">'bar'</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">,</span> <span class="token string">'world!'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token string">'baz'</span><span class="token punctuation">:</span> <span class="token boolean">True</span><span class="token punctuation">}</span>
jar <span class="token operator">=</span> open<span class="token punctuation">(</span><span class="token string">'data.pkl'</span><span class="token punctuation">,</span> <span class="token string">'wb'</span><span class="token punctuation">)</span>
pickle<span class="token punctuation">.</span>dump<span class="token punctuation">(</span>data<span class="token punctuation">,</span> jar<span class="token punctuation">)</span> <span class="token comment" spellcheck="true"># 将pickle后的数据写入jar文件</span>
jar<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>过了几个小时，我们想把它取出来，我们只需要反pickle它:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">import</span> pickle

pkl_file <span class="token operator">=</span> open<span class="token punctuation">(</span><span class="token string">'data.pkl'</span><span class="token punctuation">,</span> <span class="token string">'rb'</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true"># 与pickle后的数据连接</span>
data <span class="token operator">=</span> pickle<span class="token punctuation">.</span>load<span class="token punctuation">(</span>pkl_file<span class="token punctuation">)</span> <span class="token comment" spellcheck="true"># 把它加载进一个变量</span>
<span class="token keyword">print</span> data
pkl_file<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>将会发生什么？正如你期待的，它就是我们之前的 data 。</p>
<p>现在，还需要谨慎地说一句： pickle并不完美。Pickle文件很容易因为事故或被故意的破坏掉。Pickling或许比纯文本文件安全一些，但是依然有可能被用来运行恶意代码。而且它还不支持跨Python版本，所以不要指望分发pickle对象之后所有人都能正确地读取。然而不管怎么样，它依然是一个强有力的工具，可以用于缓存和其他类型的持久化工作。</p>
<h4 id="Pickle你的对象"><a href="#Pickle你的对象" class="headerlink" title="Pickle你的对象"></a>Pickle你的对象</h4><p>Pickle不仅仅可以用于内建类型，任何遵守pickle协议的类都可以被pickle。Pickle协议有四个可选方法，可以让类自定义它们的行为（这和C语言扩展略有不同，那不在我们的讨论范围之内）。</p>
<ul>
<li><p><code>__getinitargs__</code>(self)</p>
<p>如果你想让你的类在反pickle时调用 <code>__init__</code> ，你可以定义<code>__getinitargs__</code>(self) ，它会返回一个参数元组，这个元组会传递给<strong>init</strong>` 。注意，这个方法只能用于旧式类。</p>
</li>
<li><p><code>__getnewargs__</code>(self)</p>
<p>对新式类来说，你可以通过这个方法改变类在反pickle时传递给 <code>__new__</code> 的参数。这个方法应该返回一个参数元组。</p>
</li>
<li><p><code>__getstate__</code>(self)</p>
<p>你可以自定义对象被pickle时被存储的状态，而不使用对象的 <code>__dict__</code> 属性。 这个状态在对象被反pickle时会被 <code>__setstate__</code> 使用。</p>
</li>
<li><p><code>__setstate__</code>(self)</p>
<p>当一个对象被反pickle时，如果定义了 <code>__setstate__</code> ，对象的状态会传递给这个魔法方法，而不是直接应用到对象的 <code>__dict__</code> 属性。这个魔法方法和<code>__getstate__</code> 相互依存：当这两个方法都被定义时，你可以在Pickle时使用任何方法保存对象的任何状态。</p>
</li>
<li><p><code>__reduce__</code>(self)</p>
<p>当定义扩展类型时（也就是使用Python的C语言API实现的类型），如果你想pickle它们，你必须告诉Python如何pickle它们。 <code>__reduce__</code> 被定义之后，当对象被Pickle时就会被调用。它要么返回一个代表全局名称的字符串，Pyhton会查找它并pickle，要么返回一个元组。这个元组包含2到5个元素，其中包括：一个可调用的对象，用于重建对象时调用；一个参数元素，供那个可调用对象使用；被传递给 <code>__setstate__</code> 的状态（可选）；一个产生被pickle的列表元素的迭代器（可选）；一个产生被pickle的字典元素的迭代器（可选）；</p>
</li>
<li><p><code>__reduce_ex__</code>(self)</p>
<p><code>__reduce_ex__</code> 的存在是为了兼容性。如果它被定义，在pickle时<code>__reduce_ex__</code> 会代替 <code>__reduce__</code> 被调用。 <code>__reduce__</code> 也可以被定义，用于不支持 <code>__reduce_ex__</code> 的旧版pickle的API调用。</p>
</li>
</ul>
<h4 id="一个例子-1"><a href="#一个例子-1" class="headerlink" title="一个例子"></a>一个例子</h4><p>我们的例子是 Slate ，它会记住它的值曾经是什么，以及那些值是什么时候赋给它的。然而 每次被pickle时它都会变成空白，因为当前的值不会被存储:</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">import</span> time

<span class="token keyword">class</span> <span class="token class-name">Slate</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">'''存储一个字符串和一个变更日志的类, 每次被pickle都会忘记它当前的值'''</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>value <span class="token operator">=</span> value
        self<span class="token punctuation">.</span>last_change <span class="token operator">=</span> time<span class="token punctuation">.</span>asctime<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>history <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

        <span class="token keyword">def</span> <span class="token function">change</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> new_value<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token comment" spellcheck="true"># 改变当前值，将上一个值记录到历史</span>
            self<span class="token punctuation">.</span>history<span class="token punctuation">[</span>self<span class="token punctuation">.</span>last_change<span class="token punctuation">]</span> <span class="token operator">=</span> self<span class="token punctuation">.</span>value
            self<span class="token punctuation">.</span>value <span class="token operator">=</span> new_value<span class="token punctuation">)</span>
            self<span class="token punctuation">.</span>last_change <span class="token operator">=</span> time<span class="token punctuation">.</span>asctime<span class="token punctuation">(</span><span class="token punctuation">)</span>

            <span class="token keyword">def</span> <span class="token function">print_change</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
                <span class="token keyword">print</span> <span class="token string">'Changelog for Slate object:'</span>
                <span class="token keyword">for</span> k<span class="token punctuation">,</span>v <span class="token keyword">in</span> self<span class="token punctuation">.</span>history<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
                    <span class="token keyword">print</span> <span class="token string">'%s\t %s'</span> <span class="token operator">%</span> <span class="token punctuation">(</span>k<span class="token punctuation">,</span>v<span class="token punctuation">)</span>

                    <span class="token keyword">def</span> <span class="token function">__getstate__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
                        <span class="token comment" spellcheck="true"># 故意不返回self.value或self.last_change</span>
                        <span class="token comment" spellcheck="true"># 我们想在反pickle时得到一个空白的slate</span>
                        <span class="token keyword">return</span> self<span class="token punctuation">.</span>history

                    <span class="token keyword">def</span> <span class="token function">__setstate__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
                        <span class="token comment" spellcheck="true"># 使self.history = slate，last_change</span>
                        <span class="token comment" spellcheck="true"># 和value为未定义</span>
                        self<span class="token punctuation">.</span>history <span class="token operator">=</span> state
                        self<span class="token punctuation">.</span>value<span class="token punctuation">,</span> self<span class="token punctuation">.</span>last_change <span class="token operator">=</span> None<span class="token punctuation">,</span> None<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="如何调用魔法方法"><a href="#如何调用魔法方法" class="headerlink" title="如何调用魔法方法"></a>如何调用魔法方法</h3><p>一些魔法方法直接和内建函数对应，这种情况下，如何调用它们是显而易见的。然而，另外的情况下，调用魔法方法的途径并不是那么明显。这个附录旨在展示那些不那么明显的调用魔法方法的语法。</p>
<table>
<thead>
<tr>
<th align="left">魔法方法</th>
<th align="left">什么时候被调用</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>__new__</code>(cls [,…])</td>
<td align="left">instance = MyClass(arg1, arg2)</td>
<td align="left">`__new__在实例创建时调用</td>
</tr>
<tr>
<td align="left"><code>__init__</code>(self [,…])</td>
<td align="left">instance = MyClass(arg1,arg2)</td>
<td align="left">`__init__在实例创建时调用</td>
</tr>
<tr>
<td align="left"><code>__cmp__</code>(self)</td>
<td align="left">self == other, self &gt; other 等</td>
<td align="left">进行比较时调用</td>
</tr>
<tr>
<td align="left"><code>__pos__</code>(self)</td>
<td align="left">+self</td>
<td align="left">一元加法符号</td>
</tr>
<tr>
<td align="left"><code>__neg__</code>(self)</td>
<td align="left">-self</td>
<td align="left">一元减法符号</td>
</tr>
<tr>
<td align="left"><code>__invert__</code>(self)</td>
<td align="left">~self</td>
<td align="left">按位取反</td>
</tr>
<tr>
<td align="left"><code>__index__</code>(self)</td>
<td align="left">x[self]</td>
<td align="left">当对象用于索引时</td>
</tr>
<tr>
<td align="left"><code>__nonzero__</code>(self)</td>
<td align="left">bool(self)</td>
<td align="left">对象的布尔值</td>
</tr>
<tr>
<td align="left"><code>__getattr__</code>(self, name)</td>
<td align="left">self.name #name不存在</td>
<td align="left">访问不存在的属性</td>
</tr>
<tr>
<td align="left"><code>__setattr__</code>(self, name)</td>
<td align="left">self.name = val</td>
<td align="left">给属性赋值</td>
</tr>
<tr>
<td align="left">`__delattr_(self, name)</td>
<td align="left">del self.name</td>
<td align="left">删除属性</td>
</tr>
<tr>
<td align="left"><code>__getattribute__</code>(self,name)</td>
<td align="left">self.name</td>
<td align="left">访问任意属性</td>
</tr>
<tr>
<td align="left"><code>__getitem__</code>(self, key)</td>
<td align="left">self[key]</td>
<td align="left">使用索引访问某个元素</td>
</tr>
<tr>
<td align="left"><code>__setitem__</code>(self, key)</td>
<td align="left">self[key] = val</td>
<td align="left">使用索引给某个元素赋值</td>
</tr>
<tr>
<td align="left"><code>__delitem__</code>(self, key)</td>
<td align="left">del self[key]</td>
<td align="left">使用索引删除某个对象</td>
</tr>
<tr>
<td align="left"><code>__iter__</code>(self)</td>
<td align="left">for x in self</td>
<td align="left">迭代</td>
</tr>
<tr>
<td align="left"><code>__contains__</code>(self, value)</td>
<td align="left">value in self, value not in self</td>
<td align="left">使用in进行成员测试</td>
</tr>
<tr>
<td align="left"><code>__call__</code>(self [,…])</td>
<td align="left">self(args)</td>
<td align="left">“调用”一个实例</td>
</tr>
<tr>
<td align="left"><code>__enter__</code>(self)</td>
<td align="left">with self as x:</td>
<td align="left">with声明的上下文管理器</td>
</tr>
<tr>
<td align="left"><code>__exit__</code>(self, exc, val, trace)</td>
<td align="left">with self as x:</td>
<td align="left">with声明的上下文管理器</td>
</tr>
<tr>
<td align="left"><code>__getstate__</code>(self)</td>
<td align="left">pickle.dump(pkl_file, self)</td>
<td align="left">Pickling</td>
</tr>
<tr>
<td align="left"><code>__setstate__</code>(self)</td>
<td align="left">data = pickle.load(pkl_file)</td>
<td align="left">Pickling</td>
</tr>
</tbody></table>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>本节内容的目标是使所有阅读它的人都能有所收获，无论他们有没有使用Python或者进行面向对象编程的经验。如果你刚刚开始学习Python，你会得到宝贵的基础知识，了解如何写出具有丰富特性的，优雅而且易用的类。如果你是中级的Python程序员，你或许能掌握一些新的概念和技巧，以及一些可以减少代码行数的好办法。如果你是专家级别的Python爱好者，你又重新复习了一遍某些可能已经忘掉的知识，也可能顺便了解了一些新技巧。无论你的水平怎样，我希望这趟遨游Python特殊方法的旅行，真的对你产生了魔法般的效果。</p>

            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://zhangyafeii.gitee.io" rel="external nofollow noreferrer">张亚飞</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://zhangyafeii.gitee.io/python/base/di-24-pian-mian-xiang-dui-xiang-mo-fa-fang-fa/">https://zhangyafeii.gitee.io/python/base/di-24-pian-mian-xiang-dui-xiang-mo-fa-fang-fa/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="https://zhangyafeii.gitee.io" target="_blank">张亚飞</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/Python%E4%B9%8B%E8%B7%AF/">
                                    <span class="chip bg-color">Python之路</span>
                                </a>
                            
                                <a href="/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/">
                                    <span class="chip bg-color">面向对象</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">

<div id="article-share">
    
    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>
            
        </div>
    </div>

    
        <link rel="stylesheet" href="/libs/gitalk/gitalk.css">
<link rel="stylesheet" href="/css/my-gitalk.css">

<div class="card gitalk-card" data-aos="fade-up">
    <div class="comment_headling" style="font-size: 20px; font-weight: 700; position: relative; left: 20px; top: 15px; padding-bottom: 5px;">
        <i class="fas fa-comments fa-fw" aria-hidden="true"></i>
        <span>评论</span>
    </div>
    <div id="gitalk-container" class="card-content"></div>
</div>

<script src="/libs/gitalk/gitalk.min.js"></script>
<script>
    let gitalk = new Gitalk({
        clientID: 'b9bf0e29501275f4f23c',
        clientSecret: '1c56bc88fdb7b23ee3712916a38e5e8d1311823a',
        repo: 'github.io',
        owner: 'zhangyafeii',
        admin: "zhangyafeii",
        id: '2020-12-24T10-00-01',
        distractionFreeMode: false  // Facebook-like distraction free mode
    });

    gitalk.render('gitalk-container');
</script>
    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/python/base/di-25-pian-mian-xiang-dui-xiang-bian-cheng-ying-yong/">
                    <div class="card-image">
                        
                        <img src="https://zhangyafei-1258643511.cos.ap-nanjing.myqcloud.com/Python%E6%96%87%E6%A1%A3/assets/25.jpg" class="responsive-img" alt="第25篇：面向对象编程应用">
                        
                        <span class="card-title">第25篇：面向对象编程应用</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            面向对象编程对初学者来说不难理解但很难应用，虽然我们为大家总结过面向对象的三步走方法（定义类、创建对象、给对象发消息），但是说起来容易做起来难。大量的编程练习和阅读优质的代码可能是这个阶段最能够帮助到大家的两件事情。接下来我们还是通过经典的
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2020-12-25
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/Python/" class="post-category">
                                    Python
                                </a>
                            
                            <a href="/categories/Python/base/" class="post-category">
                                    base
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Python%E4%B9%8B%E8%B7%AF/">
                        <span class="chip bg-color">Python之路</span>
                    </a>
                    
                    <a href="/tags/%E5%BA%94%E7%94%A8/">
                        <span class="chip bg-color">应用</span>
                    </a>
                    
                    <a href="/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/">
                        <span class="chip bg-color">面向对象</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/python/base/di-23-pian-mian-xiang-dui-xiang-bian-cheng-jin-jie/">
                    <div class="card-image">
                        
                        <img src="https://zhangyafei-1258643511.cos.ap-nanjing.myqcloud.com/Python%E6%96%87%E6%A1%A3/assets/23.jpg" class="responsive-img" alt="第23篇：面向对象编程进阶">
                        
                        <span class="card-title">第23篇：面向对象编程进阶</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            学习目标：掌握Python面向对象进阶知识及应用
一、类的成员类的成员分为三大类：字段、属性和方法。

所有成员中，只有实例字段的内容保存在对象中，即：根据此类创建了多少对象，在内存中就有多少个实例字段。而其他的成员，则都是保存在类中，即：
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2020-12-23
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/Python/" class="post-category">
                                    Python
                                </a>
                            
                            <a href="/categories/Python/base/" class="post-category">
                                    base
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Python%E4%B9%8B%E8%B7%AF/">
                        <span class="chip bg-color">Python之路</span>
                    </a>
                    
                    <a href="/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/">
                        <span class="chip bg-color">面向对象</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>


<script>
    $('#articleContent').on('copy', function (e) {
        // IE8 or earlier browser is 'undefined'
        if (typeof window.getSelection === 'undefined') return;

        var selection = window.getSelection();
        // if the selection is short let's not annoy our users.
        if (('' + selection).length < Number.parseInt('120')) {
            return;
        }

        // create a div outside of the visible area and fill it with the selected text.
        var bodyElement = document.getElementsByTagName('body')[0];
        var newdiv = document.createElement('div');
        newdiv.style.position = 'absolute';
        newdiv.style.left = '-99999px';
        bodyElement.appendChild(newdiv);
        newdiv.appendChild(selection.getRangeAt(0).cloneContents());

        // we need a <pre> tag workaround.
        // otherwise the text inside "pre" loses all the line breaks!
        if (selection.getRangeAt(0).commonAncestorContainer.nodeName === 'PRE') {
            newdiv.innerHTML = "<pre>" + newdiv.innerHTML + "</pre>";
        }

        var url = document.location.href;
        newdiv.innerHTML += '<br />'
            + '来源: 飞凡空间<br />'
            + '文章作者: 张亚飞<br />'
            + '文章链接: <a href="' + url + '">' + url + '</a><br />'
            + '本文章著作权归作者所有，任何形式的转载都请注明出处。';

        selection.selectAllChildren(newdiv);
        window.setTimeout(function () {bodyElement.removeChild(newdiv);}, 200);
    });
</script>


<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>

    
<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


<!-- 代码块折行 -->

<style type="text/css">
code[class*="language-"], pre[class*="language-"] { white-space: pre !important; }
</style>

    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            // headingsOffset: -205,
            headingSelector: 'h2, h3, h4'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h2, h3, h4').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>



    <footer class="page-footer bg-color">
    <div class="container row center-align">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            <span id="year">年份</span>
            <a href="https://zhangyafeii.gitee.io" target="_blank">张亚飞</a>
            |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>
            
            &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                class="white-color">344.5k</span>&nbsp;字
            
            
            
            
            
            
            <span id="busuanzi_container_site_pv">
                |&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;<span id="busuanzi_value_site_pv"
                    class="white-color"></span>&nbsp;次
            </span>
            
            
            <span id="busuanzi_container_site_uv">
                |&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;<span id="busuanzi_value_site_uv"
                    class="white-color"></span>&nbsp;人
            </span>
            
            <br>
            
            <span id="sitetime">载入运行时间...</span>
            <script>
                function siteTime() {
                    window.setTimeout("siteTime()", 1000);
                    var seconds = 1000;
                    var minutes = seconds * 60;
                    var hours = minutes * 60;
                    var days = hours * 24;
                    var years = days * 365;
                    var today = new Date();
                    var startYear = "2020";
                    var startMonth = "12";
                    var startDate = "18";
                    var startHour = "12";
                    var startMinute = "12";
                    var startSecond = "12";
                    var todayYear = today.getFullYear();
                    var todayMonth = today.getMonth() + 1;
                    var todayDate = today.getDate();
                    var todayHour = today.getHours();
                    var todayMinute = today.getMinutes();
                    var todaySecond = today.getSeconds();
                    var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond);
                    var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond);
                    var diff = t2 - t1;
                    var diffYears = Math.floor(diff / years);
                    var diffDays = Math.floor((diff / days) - diffYears * 365);
                    var diffHours = Math.floor((diff - (diffYears * 365 + diffDays) * days) / hours);
                    var diffMinutes = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours) /
                        minutes);
                    var diffSeconds = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours -
                        diffMinutes * minutes) / seconds);
                    if (startYear == todayYear) {
                        document.getElementById("year").innerHTML = todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffDays + " 天 " + diffHours +
                            " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    } else {
                        document.getElementById("year").innerHTML = startYear + " - " + todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffYears + " 年 " + diffDays +
                            " 天 " + diffHours + " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    }
                }
                setInterval(siteTime, 1000);
            </script>
            
            <br>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/zhangyafeii" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="https://gitee.com/zhangyafeii" class="tooltipped" target="_blank" data-tooltip="访问我的Gitee" data-position="top" data-delay="50">
        <i class="fab fa-google-plus"></i>
    </a>



    <a href="mailto:zhangyafeii@foxmail.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=1271570224" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 1271570224" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>







    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script src="/js/search.js"></script>
<script type="text/javascript">
$(function () {
    searchFunc("/" + "search.xml", 'searchInput', 'searchResult');
});
</script>
    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>
    <script src="/js/cursor.js"></script>

    <!-- Global site tag (gtag.js) - Google Analytics -->


    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    

    

    
    <script type="text/javascript" src="/libs/background/ribbon-dynamic.js" async="async"></script>
    
    
    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
